/* ***** BEGIN LICENSE BLOCK *****
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is lutsko.com code.
 *
 * The Initial Developer of the Original Code is  James Lutsko 
 * (jim@lutsko.com http://www.lutsko.com).
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the NPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the NPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

package autoschedule;

import java.util.Iterator;
import java.util.Vector;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Calendar;

import autoteam.Local;

public class Facility implements Serializable, Comparable
{
    String name;

    /** Used to sort the facilities*/
    public transient Integer rank = null;

    Vector games = null;

    boolean sorted = true;

    Vector availability; // each entry a Day

    Vector _availability;

    public String getName(){ return name;}

    public void sortGames() 
    {
	Collections.sort(games, new Comparator() {
		public int compare(Object o1, Object o2) {
		    if(o1 == o2) return 0;
		    Game g1 = (Game) o1;
		    Game g2 = (Game) o2;
		    
		    int d1 = g1.getDayOfYear();
		    int d2 = g2.getDayOfYear();
		    
		    int ret = (d1 < d2 ? -1 : (d1 == d2 ? 0 : 1)); 
		    if(ret == 0)
			ret = g1.getFacilityName().compareTo(g2.getFacilityName());
		    if(ret == 0)
			ret = g1.time.compareTo(g2.time);
		    return ret;
		}});
    }

    public void addDay(Day d)
    {
	System.out.println("DAY = " + d);
	availability.add(d); 
	sorted = false;

	if(_availability == null) _availability = new Vector();
	Day d1 = new Day(d);
	d1.initialize();
	_availability.add(d1);

    }
    public void removeDay(int d)
    {
	Day d0 = (Day) availability.get(d); 
	availability.removeElementAt(d); //"sorted" is unchanged
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d1 = (Day) I.next();		
		if(d1.toString().compareTo(d0.toString()) == 0)
		    d1.unScheduleAll();
	    }
    }
    public Day getDay(int r){ return (Day) availability.get(r);}
    public int getAvailabilitySize(){ return availability.size();}


    public int getNumberOfGames(){return (games == null ? 0 : games.size());}
    public Game getGame(int r){return (games == null ? null : (Game) games.get(r));}

    public void initialize()
    {
	if(_availability == null) _availability = new Vector();
	else _availability.clear();

	if(games != null) games.clear();

	for(Iterator I = availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();		
		Day d1 = new Day(d);
		d1.initialize();
		_availability.add(d1);
	    }
    }


    // durtion is in minutes
    Day getEarliestBlock(int duration, Date starting, Integer simulGames)
    {
	sortAvailability();
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();		
		//		System.out.println("starting = " + starting + " day = " + d);
		//		if(starting == null || !d.date.before(starting))
		if(starting == null || !d.before(starting))
		    if(d.hasTimeSlot(simulGames,duration))
			return d;
	    }
	return null;
    }

    boolean areConsecutiveGames(Game g1, Game g2)
    {
	sortAvailability();

	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();
		if(d.games == null) continue;

		for(Iterator J = d.games.iterator();J.hasNext();)
		    {
			TimeBlock t = (TimeBlock) J.next();
			if(t.g == g1 || t.g == g2) 
			    {
				if(J.hasNext())
				    {
					TimeBlock t1 = (TimeBlock) J.next();
					if(t1.g == g2 || t1.g == g1)
					    return true;
					else return false;
				    } else return false;
			    }
		    }
	    }
	return false;
    }

    void removeGame(Game g)
    {
	sortAvailability();

	TimeBlock tb = null;
	Day day = null;
	for(Iterator I = _availability.iterator();I.hasNext() && (tb == null);)
	    {
		Day d = (Day) I.next();
		if(d.games == null) continue;

		for(Iterator J = d.games.iterator();J.hasNext() && (tb == null);)
		    {
			TimeBlock t = (TimeBlock) J.next();
			if(t.g == g) {tb = t;day = d;}
		    }
	    }
	// should never happen
	if(tb == null) return;

	tb.g = null;
	if(day == null)
	    {
		// should never happen
		// create day
		_availability.add(new Day(g.getDate(),tb));
	    } else {
		day.games.remove(tb);
		day.addOpenBlock(tb);
	    }
	games.remove(g);
    }

    void replaceGame(Game g1, Game g2)
    {
	sortAvailability();

	TimeBlock tb = null;
	Day day = null;
	for(Iterator I = _availability.iterator();I.hasNext() && (tb == null);)
	    {
		Day d = (Day) I.next();
		if(d.games == null) continue;

		for(Iterator J = d.games.iterator();J.hasNext() && (tb == null);)
		    {
			TimeBlock t = (TimeBlock) J.next();
			if(t.g == g1) {tb = t;day = d;}
		    }
	    }
	// should never happen
	if(tb == null || day == null) return;

	tb.g = g2;

	int i = games.indexOf(g1);
	games.setElementAt(g2,i);

    }


    public void setStart(Day d, Integer val)
    {
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d1 = (Day) I.next();		
		if(d1.toString().compareTo(d.toString()) == 0)
		    {
			d1.unScheduleBefore(val);
			d1.setStart(val);
		    }
	    }
	d.setStart(val);	
    }

    public void setEnd(Day d, Integer val)
    {
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d1 = (Day) I.next();		
		if(d1.toString().compareTo(d.toString()) == 0)
		    {
			d1.unScheduleAfter(val);
			d1.setEnd(val);
		    }
	    }
	d.setEnd(val);	
    }

    public void setDate(Day d, Date dt)
    {
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d1 = (Day) I.next();		
		if(d1.toString().compareTo(d.toString()) == 0)
		    d1.setDate(dt);
	    }
	d.setDate(dt);
    }

    // duration is in minutes
    public void schedule(Game g, Day gDay, int duration, Integer simulGames,AutoScheduleDoc doc)
    {
	sortAvailability();
	if(games == null) games = new Vector();

	gDay.scheduleGame(g,duration,simulGames,doc);
	g.setFacility(this);
	games.add(g);

    }

    // duration is in minutes
    public void schedule(Game g, Day gDay, AutoScheduleDoc doc)
    {
	sortAvailability();
	if(games == null) games = new Vector();

	League l = g.league;
	int duration = l.getDuration();
	Integer simulGames = (Integer) l.facilityCapacity.get(this);

	gDay.scheduleGame(g,duration,simulGames,doc);
	g.setFacility(this);
	games.add(g);

    }

    public Facility(String _name)
    {
	name = _name;
	availability = new Vector();
    }
    public Facility()
    {
	availability = new Vector();
    }

    public int compareTo(Object o)
    {
	Facility f = (Facility) o;
	return rank.compareTo(f.rank);
    }

    public String toString(){return name;}

    public void logAvailability(AutoScheduleDoc doc)
    {
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();		
		d.logAvailability(doc);
	    }

    }

    public int getOpenBlocks()
    {
	if(_availability == null)
	    initialize();

	int blocks = 0;
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();		
		if(d.open == null) continue;
		blocks += d.open.size();
	    }
	return blocks;
    }

    public Day getDayFromBlock(int b)
    {
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();		
		if(d.open == null) continue;
		if(b < d.open.size())
		    return (Day) d;
		b -= d.open.size();
	    }
	return null;
    }

    public void killBlock(int b)
    {
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();		
		if(d.open == null) continue;
		if(b < d.open.size())
		    {
			d.open.remove(b);
			return;
		    }
		b -= d.open.size();
	    }
	return;
    }

    public Time getBlockStart(int b)
    {
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();		
		if(d.open == null) continue;
		if(b < d.open.size())
		    {
			TimeBlock tb = (TimeBlock) d.open.get(b);
			return tb.a;
		    } else b -= d.open.size();
	    }
	return null;
    }


    public Time getBlockEnd(int b)
    {
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();		
		if(d.open == null) continue;
		if(b < d.open.size())
		    {
			TimeBlock tb = (TimeBlock) d.open.get(b);
			return tb.b;
		    }  else b -= d.open.size();
	    }
	return null;
    }


    public Double getCapacity(int b)
    {
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();		
		if(d.open == null) continue;
		if(b < d.open.size())
		    {
			TimeBlock tb = (TimeBlock) d.open.get(b);
			return new Double(tb.capacity);
		    }  else b -= d.open.size();
	    }
	return null;
    }


    public void sortAvailability()
	{
	    if(sorted == false)
		{
		    Collections.sort(availability);
		    if(_availability != null) Collections.sort(_availability);
		    sorted = true;
		    sorted = true;
		}
	}

    public void consolidate()
    {
	for(Iterator I = _availability.iterator();I.hasNext();)
	    {
		Day d = (Day) I.next();		
		d.doConsolidate();
	    }
    }

    static final long serialVersionUID = -7760314101531928595L;
}
